package gov.nasa.anml.lifted;

import java.util.ArrayList;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.State;
import gov.nasa.anml.PDDL.Time;
import gov.nasa.anml.utility.*;

//change expressions get broken down by tree walker
//nah; instead should have beginChange and endChange classes,
//analogous with, respectively, Assign and Unlock in semantics
//and in implementation, Undefine and Assign
public class Assign<S extends SimpleObject<? super S>> extends BinaryExpression<S,S,S> {

	public Assign(Expression<S,S> l,Expression<S,?> r) {
		super(l,r);
	}

	public boolean apply(State p, int contextID, State c) {
		History<S> place = left.storage(p,c);
		// will throw nullpointerexception if left is not an l-value
//		if (place.locked)
//		return false;

		History<S> last = left.storage(p,p);
		int bra = c.bra;
		SimpleFloat start = c.start;
		// do generation of child from parent by copying and advancing time
		// (bra from before to at, simulate, to after, simulate,
		//  start to next time bra to before, simulate, done)

		place.value.assign(right.value(p));
//		place.locked = true;
		place.prev = last;
		place.start = start;
		place.bra = bra;
		return true;
	}

	public S value(State s) {
		return right.value(s);
	}

	public void translateStmt(PDDL pddl, Interval unit, PDDL.Time part) {
		ArrayList<PDDL.BooleanExpression> conditions = unit.getPDDLConditions(); 
		ArrayList<PDDL.Effect> effects = unit.getPDDLEffects();
		switch(left.typeCode()) {
		case Boolean:
			assert right.typeCode() == TypeCode.Boolean : "No operation combines booleans and other types directly";
			PDDL.PredicateReference refB = (PDDL.PredicateReference) left.translateLValue(pddl,unit);
			if (right instanceof SimpleBoolean) {
				boolean v = ((SimpleBoolean)right).v;
				switch(part) {
				case Start:
				case End:
					effects.add(pddl.makeEffect(part,refB,v));
					break;
				case Interim:
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,v));
					conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refB,v)));
					break;
				case All:
				case EndHalf:
					effects.add(pddl.makeEffect(PDDL.Time.End,refB,v));
				case StartHalf:
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,v));
					conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refB,v)));
					break;
				default:
					System.err.println("New PDDL.Time constant unaccounted for in Assign.");
				}
			} else {
				PDDL.BooleanExpression r = (PDDL.BooleanExpression) right.translateExpr(pddl,unit);
				switch(part) {
				case Start:
				case End:
					effects.add(pddl.makeEffect(part,refB,r));
					break;
				case Interim:
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,r));
					conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refB,r)));
					break;
				case All:
				case EndHalf:
					effects.add(pddl.makeEffect(PDDL.Time.End,refB,r));
				case StartHalf:
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,r));
					conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refB,r)));
					break;
				default:
					System.err.println("New PDDL.Time constant unaccounted for in Assign.");
				}
			}
			break;
		case Float:
			assert right.typeCode() == TypeCode.Float : "No operation combines floats and non-floats (at present).";
			PDDL.FunctionReference refF = (PDDL.FunctionReference) left.translateLValue(pddl,unit);
			PDDL.FloatExpression v = (PDDL.FloatExpression) right.translateExpr(pddl,unit);
			switch(part) {
			case Start:
			case End:
				effects.add(pddl.makeEffect(part,refF,v));
				break;
			case Interim:
				effects.add(pddl.makeEffect(PDDL.Time.Start,refF,v));
				conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refF,v)));
				break;
			case All:
			case EndHalf: // PDDL can't do effects just after start, so I settle for at start.
				effects.add(pddl.makeEffect(PDDL.Time.End,refF,v));
			case StartHalf:
				effects.add(pddl.makeEffect(PDDL.Time.Start,refF,v));
				conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refF,v)));
				break;
			default:
				System.err.println("New PDDL.Time constant unaccounted for in Assign.");
			}
			break;
		default:
			System.err.println("Oops!");
		}
	}

}
